PRINT FORM OR CLIENT AREA TO SIZE ON POSTSCRIPT/PCL Q85978 Summary: The Visual Basic PrintForm method provides a way to print the client area of a form. However, PrintForm does not allow you to control the size or proportion of the printed output, or to print the non-client area (the caption and border) of the form. An alternative method which allows printing of the entire form as well as control over size and placement is detailed in another article in the Knowledge Base. For more information on this topic, query on the following words in the Microsoft Knowledge Base: caption and printform and client This method however, uses the API function StretchBlt which does not work on PostScript printers. The following code example uses the StretchDIBits function as well as other Windows API functions to print the entire form, and provides a method to control the size of the output. This method works on both Postscript and PCL (printer control language) or HP-type LaserJet printers. It can also be used to print only the client area to a specific size and to control the position of the printed form on the page. With slight modification, it can be used to print all the forms in a project or any window whose handle is known. This information applies to the Microsoft Visual Basic programming system version 1.0 for Windows. More Information: This example uses the GetWindowDC and CreateCompatibleDC functions to get the DC (device context) for the entire form and create a DC compatible with the form's DC. It then uses the GetDeviceCaps function to get the pixel dimensions. A bitmap compatible with the device context of the form is created with the CreateCompatibleBitmap function. The bitmap is then selected into the compatible DC using the SelectObject function and the actual image is copied to the bitmap using the BitBlt function. All the parameters for the bitmap are set into the user-defined type BitmapInfo. The number of bytes needed for a buffer are calculated and then allocated using the GlobalAlloc function, and then locked using the GlobalLock function. Then the GetDIBits call copies the bitmap to the buffer and the StretchDIBits call copies the bitmap from the buffer to the printer's device context. 1. To create the example add the following controls to a form and set their properties to the following: Form Property Setting --------------------------------------------- Form Formname Form1 Command Controlname Command1 Command Controlname Command2 2. Add the following code to the Global module: '********************************************************************* '* Project '* PrintAll.MAK '* '* Contents '* PrintALL.GLB '* PrintALL.FRM (Form1) '* PrintALL.BAS '* Structure '* Form1 can contain any number of controls. '* The minimum number to demonstrate both client area '* printing and entire form printing is two command buttons. '* For illustration, assign a large bitmap to the picture '* property form. '* '* Description: '* This example successfully prints on both PostScript and '* PCL (Printer Control Language: the non-PostScript type) '* printers. The printer output is of the same resolution as '* you would expect from the PrintForm method or from '* printing the form from the VB environment. Both the '* PrintClient and PrintWindow procedures are generic in '* that they can be used to print any visible window. To '* incorporate the code into your project, add PrintAll.BAS '* in the project and paste the code in the PrintAll.GLB '* program into your Global module. The code in the '* Command1_Click and Command2_Click events demonstrates how '* to call the two procedures. To print any active window, '* use the Appactivate and the GetFocus functions to get the '* handle to the window to pass to the procedures. '* '********************************************************************* '********************************************************************* '* Module '* PrintAll.GLB '* '* Description: '* Contains all the necessary Type structure declarations '* needed by the API functions declared in PrintAll.BAS. '********************************************************************* DefInt A-Z Type BITMAPINFOHEADER_Type biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type Type BITMAPINFO_Type BitmapInfoHeader As BITMAPINFOHEADER_Type bmiColors As String * 1024 End Type Type RectType Left As Integer Top As Integer Right As Integer Bottom As Integer End Type Type PointType x As Integer y As Integer End Type 3. Add the following code to the general declarations level of a general module (.BAS file): Note: All declarations must be complete on one line. '********************************************************************* '* Module '* PrintAll.BAS '********************************************************************* DefInt A-Z Declare Function GetDesktopWindow Lib "user" () ' DC related API Declare Function CreateCompatibleDC Lib "gdi" (ByVal hDC) Declare Function GetWindowDC Lib "user" (ByVal hWnd) Declare Function GetDC Lib "user" (ByVal hWnd) Declare Function ReleaseDC Lib "user" (ByVal hWnd, ByVal hDC) Declare Function DeleteDC Lib "gdi" (ByVal hDC) ' Graphics related API Declare Function BitBlt Lib "gdi" (ByVal hDC, ByVal x, ByVal y, ByVal w, ByVal h, ByVal hDC, ByVal x, ByVal y, ByVal o As Long) Declare Function GetDIBits Lib "gdi" (ByVal hDC, ByVal hBitmap, ByVal nStartScan, ByVal nNumScans, ByVal lpBits As Long, BitmapInfo As BITMAPINFO_Type, ByVal wUsage) Declare Function StretchDIBits Lib "gdi" (ByVal hDC, ByVal DestX, ByVal DestY, ByVal wDestWidth, ByVal wDestHeight, ByVal SrcX, ByVal SrcY, ByVal wSrcWidth, ByVal wSrcHeight, ByVal lpBits&, BitsInfo As BITMAPINFO_Type, ByVal wUsage, ByVal dwRop&) ' General attribute related API Declare Function GetDeviceCaps Lib "gdi" (ByVal hDC, ByVal nIndex) Declare Function GetWindowRect Lib "user" (ByVal hWnd, lpRect As RectType) Declare Function GetClientRect Lib "user" (ByVal hWnd, lpRect As RectType) ' Memory allocation related API Declare Function GlobalAlloc Lib "kernel" (ByVal wFlags, ByVal lMem&) Declare Function GlobalLock Lib "kernel" (ByVal hMem) As Long Declare Function GlobalUnlock Lib "kernel" (ByVal hMem) Declare Function GlobalFree Lib "kernel" (ByVal hMem) ' Graphics object related API Declare Function CreateCompatibleBitmap Lib "gdi" (ByVal hDC, ByVal nWidth, ByVal nHeight) Declare Function DeleteObject Lib "gdi" (ByVal hObject) Declare Function SelectObject Lib "gdi" (ByVal hDC, ByVal hObject) Declare Function ClientToScreen Lib "user" (ByVal hWnd, As PointType) Declare Function LPToDP Lib "gdi" (ByVal hDC, p As PointType, ByVal nCount) Const False = 0 Const True = Not False Const HORZRES = 8 Const VERTRES = 10 Const SRCCOPY = &HCC0020 Const NEWFRAME = 1 Const BITSPIXEL = 12 Const PLANES = 14 Const BI_RGB = 0 Const BI_RLE8 = 1 Const BI_RLE4 = 2 Const DIB_PAL_COLORS = 1 Const DIB_RGB_COLORS = 0 Const GMEM_MOVEABLE = 2 4. Add the following function, PrintWindow to a basic module (.BAS file): Note: The function statement should be on one line. '********************************************************************* '* Title '* PrintWindow() '* '* Description '* '* Copies the entire window visible on the desktop to another '* window or device such as a printer. This routine is capable of '* printing complete form images on any printer that has Windows '* drivers loaded including Postscript. This routine will work if '* the window is directly on the desktop and is not minimized '* (or iconic). '* '* The API functions GetDiBits and StretchBits are used to copy '* the client area image to the destination device. '* '* Parameters: '* hDC_Dest Handle to the DC of the destination device or '* window. '* DestX X position of where the image will be '* displayed on the destination device. '* DestY Y position of where the image will be '* displayed on the destination device. '* DestDevWidth Pixel width of the destination device. '* DestDevHeight Pixel height of the destination device. '* hWnd_SrcWindow Window handle of the source window to be '* displayed on the destination device. '********************************************************************* Function PrintWindow (ByVal hDC_Dest, ByVal DestX, ByVal DestY, ByVal DestDevWidth, ByVal DestDevHeight, ByVal hWnd_SrcWindow) Dim Rect As RectType Dim BitmapInfo As BITMAPINFO_Type cr$ = Chr$(13) ' Get the DC for the entire window including the non-client area. hDC_Window = GetWindowDC(hWnd_SrcWindow) hDC_Mem = CreateCompatibleDC(hDC_Window) ' Get the pixel dimensions of the screen. ScreenWidth = GetDeviceCaps(hDC_Window, HORZRES) ScreenHeight = GetDeviceCaps(hDC_Window, VERTRES) ' Get the pixel dimensions of the window to be printed. r = GetWindowRect(hWnd_SrcWindow, Rect) Window_Width = Abs(Rect.Right - Rect.Left) Window_Height = Abs(Rect.Bottom - Rect.Top) ' Create a bitmap compatible with the window DC. hBmp_Window = CreateCompatibleBitmap(hDC_Window, Window_Width, Window_Height) ' Select the bitmap to hold the window image into the memory DC. hPrevBmp = SelectObject(hDC_Mem, hBmp_Window) ' Copy the image of the window to the memory DC. r1 = BitBlt(hDC_Mem, 0, 0, Window_Width, Window_Height, hDC_Window, 0, 0, SRCCOPY) BitsPerPixel = GetDeviceCaps(hDC_Mem, BITSPIXEL) ColorPlanes = GetDeviceCaps(hDC_Mem, PLANES) BitmapInfo.BitmapInfoHeader.biSize = 40 BitmapInfo.BitmapInfoHeader.biWidth = Window_Width BitmapInfo.BitmapInfoHeader.biHeight = Window_Height BitmapInfo.BitmapInfoHeader.biPlanes = 1 BitmapInfo.BitmapInfoHeader.biBitCount = BitsPerPixel * ColorPlanes BitmapInfo.BitmapInfoHeader.biCompression = BI_RGB BitmapInfo.BitmapInfoHeader.biSizeImage = 0 BitmapInfo.BitmapInfoHeader.biXPelsPerMeter = 0 BitmapInfo.BitmapInfoHeader.biYPelsPerMeter = 0 BitmapInfo.BitmapInfoHeader.biClrUsed = 0 BitmapInfo.BitmapInfoHeader.biClrImportant = 0 ' Calculate the ratios based on the source and destination ' devices. This will help to cause the size of the window image ' to be approximately the same proportion on another device ' such as a printer. WidthRatio! = Window_Width / ScreenWidth HeightAspectRatio! = Window_Height / Window_Width PrintWidth = WidthRatio! * DestDevWidth PrintHeight = HeightAspectRatio! * PrintWidth ' Calculate the number of bytes needed to store the image assuming ' 8 bits/pixel. BytesNeeded& = CLng(Window_Width + 1) * (Window_Height + 1) ' Allocate a buffer to hold the bitmap bits. hMem = GlobalAlloc(GMEM_MOVEABLE, BytesNeeded&) If hDC_Window <> 0 And hBmp_Window <> 0 And hDC_Dest <> 0 And hMem <> 0 Then lpBits& = GlobalLock(hMem) ' Get the bits that make up the image and copy them to the ' destination device. r2 = GetDIBits(hDC_Mem, hBmp_Window, 0, Window_Height, lpBits&, BitmapInfo, DIB_RGB_COLORS) r3 = StretchDIBits(hDC_Dest, DestX, DestY, PrintWidth, PrintHeight, 0, 0, Window_Width, Window_Height, lpBits&, BitmapInfo, DIB_RGB_COLORS, SRCCOPY) Else PrintWindow = False Exit Function End If ' Reselect in the previous bitmap and select out the source ' image bitmap. r = SelectObject(hDC_Mem, hPrevBmp) ' Release or delete DC's, memory and objects. r = GlobalUnlock(hMem) r = GlobalFree(hMem) r = DeleteDC(hDC_Window) r = DeleteObject(hBmp_Window) r = ReleaseDC(hWnd_SrcWindow, hDC_Form) ' Return true if the window was successfully printed. If r2 <> 0 And r3 <> 0 Then PrintWindow = True Else PrintWindow = False End If End Function 5. Add the following function, PrintClient to a basic file (.BAS file): '********************************************************************* '* Title '* PrintClient() '* '* Description '* '* Copies the client area of a window visible on the desktop to '* another window or device such as a printer. This routine is '* capable of printing client area images on any printer that has '* Windows drivers loaded including PostScript. The routine will '* work if if the window is directly on the desktop and is not '* minimized (or iconic). '* '* The API functions GetDiBits and StretchBits are used to copy '* the client area image to the destination device. '* '* Parameters: '* hDC_Dest Handle to the DC of the destination device or '* window. '* DestX X position of where the image will be '* displayed on the destination device. '* DestY Y position of where the image will be '* displayed on the destination device. '* DestDevWidth Pixel width of the destination device. '* DestDevHeight Pixel height of the destination device. '* hWnd_SrcWindow Window handle of the source window to be '* displayed on the destination device. '********************************************************************* Function PrintClient (ByVal hDC_Dest, ByVal DestX, ByVal DestY, ByVal DestDevWidth, ByVal DestDevHeight, ByVal hWnd_SrcWindow) Dim Rect As RectType, RectClient As RectType Dim BitmapInfo As BITMAPINFO_Type '* Dim pWindow As PointType, pClient As PointType, pDiff As PointType '* cr$ = Chr$(13) ' Get the DC for the entire window including the non-client area. hDC_Window = GetWindowDC(hWnd_SrcWindow) hDC_Mem = CreateCompatibleDC(hDC_Window) ' Get the pixel dimensions of the screen. ScreenWidth = GetDeviceCaps(hDC_Window, HORZRES) ScreenHeight = GetDeviceCaps(hDC_Window, VERTRES) ' Get the pixel dimensions of the window to be printed. r = GetWindowRect(hWnd_SrcWindow, Rect) Window_Width = Abs(Rect.Right - Rect.Left) Window_Height = Abs(Rect.Bottom - Rect.Top) ' Create a bitmap compatible with the window DC. hBmp_Window = CreateCompatibleBitmap(hDC_Window, Window_Width, Window_Height) ' Select the bitmap to hold the window image into the memory DC. hPrevBmp = SelectObject(hDC_Mem, hBmp_Window) ' Copy the image of the window to the memory DC. r1 = BitBlt(hDC_Mem, 0, 0, Window_Width, Window_Height, hDC_Window, 0, 0, SRCCOPY) '* Code specific to PrintClient '********************************************************************* ' Get the dimensions of the client area. r = GetClientRect(hWnd_SrcWindow, RectClient) Client_Width = Abs(RectClient.Right - RectClient.Left) Client_Height = Abs(RectClient.Bottom - RectClient.Top) ' Calculate the pixel difference (x and y) between the upper-left ' corner of the non-client area and the upper-left corner of the ' client area. pClient.x = RectClient.Left pClient.y = RectClient.Top r = ClientToScreen(hWnd_SrcWindow, pClient) xDiff = Abs(pClient.x - Rect.Left) yDiff = Abs(pClient.y - Rect.Top) ' Create a DC and bitmap to represent the client area of the ' window. hDC_MemClient = CreateCompatibleDC(hDC_Window) hBmp_Client = CreateCompatibleBitmap(hDC_Window, Client_Width, Client_Height) hBmpClientPrev = SelectObject(hDC_MemClient, hBmp_Client) ' Bitblt client area of window to memory bitmap representing the ' client area. r = BitBlt(hDC_MemClient, 0, 0, Client_Width, Client_Height, hDC_Mem, xDiff, yDiff, SRCCOPY) ' Reselect in the previous bitmap and select out the source ' image bitmap. r = SelectObject(hDC_Mem, hPrevBmp) ' Delete the DC a and bitmap associated with the window. r = DeleteDC(hDC_Window) r = DeleteObject(hBmp_Window) '********************************************************************* BitsPerPixel = GetDeviceCaps(hDC_MemClient, BITSPIXEL) ColorPlanes = GetDeviceCaps(hDC_MemClient, PLANES) BitmapInfo.BitmapInfoHeader.biSize = 40 BitmapInfo.BitmapInfoHeader.biWidth = Client_Width BitmapInfo.BitmapInfoHeader.biHeight = Client_Height BitmapInfo.BitmapInfoHeader.biPlanes = 1 BitmapInfo.BitmapInfoHeader.biBitCount = BitsPerPixel * ColorPlanes BitmapInfo.BitmapInfoHeader.biCompression = BI_RGB BitmapInfo.BitmapInfoHeader.biSizeImage = 0 BitmapInfo.BitmapInfoHeader.biXPelsPerMeter = 0 BitmapInfo.BitmapInfoHeader.biYPelsPerMeter = 0 BitmapInfo.BitmapInfoHeader.biClrUsed = 0 BitmapInfo.BitmapInfoHeader.biClrImportant = 0 ' Calculate the ratios based on the source and destination ' devices. This will help to cause the size of the window image to ' be approximates the same proportion on another device such as ' a printer. WidthRatio! = Client_Width / ScreenWidth HeightAspectRatio! = Client_Height / Client_Width PrintWidth = WidthRatio! * DestDevWidth PrintHeight = HeightAspectRatio! * PrintWidth ' Calculate the number of bytes needed to store the image assuming ' 8 bits/pixel. BytesNeeded& = CLng(Window_Width + 1) * (Window_Height + 1) ' Allocate a buffer to hold the bitmap bits. hMem = GlobalAlloc(GMEM_MOVEABLE, BytesNeeded&) If hDC_Window <> 0 And hBmp_Window <> 0 And hDC_Dest <> 0 And hMem <> 0 Then lpBits& = GlobalLock(hMem) ' Get the bits that make up the image and copy them to the ' destination device. r2 = GetDIBits(hDC_MemClient, hBmp_Client, 0, Client_Height, lpBits&, BitmapInfo, DIB_RGB_COLORS) r3 = StretchDIBits(hDC_Dest, DestX, DestY, PrintWidth, PrintHeight, 0, 0, Client_Width, Client_Height, lpBits&, BitmapInfo, DIB_RGB_COLORS, SRCCOPY) Else PrintClient = False Exit Function End If ' Select in the previous bitmap. r = SelectObject(hDC_MemClient, hBmpClientPrev) ' Release or delete DC's, memory and objects. r = GlobalUnlock(hMem) r = GlobalFree(hMem) r = DeleteDC(hDC_MemClient) r = DeleteObject(hBmp_Client) r = ReleaseDC(hWnd_SrcWindow, hDC_Form) ' Return true if the window was successfully printed. If r2 <> 0 And r3 <> 0 Then PrintClient = True Else PrintClient = False End If End Function 6. Add DefInt A-Z to the general declarations level of Form1. 7. Add the following code to the Command1_Click event: Sub Command1_Click () ' The ScaleMode must be set to pixels for the PrintWindow ' routine to print correctly. Printer.ScaleMode = 3 ' Change MousePointer to an hourglass. Screen.MousePointer = 11 ' Initialize the printer. Printer.Print "" ' Establish the location of the printed image. Printer.CurrentX = 100 Printer.CurrentY = 100 ' Copy the image of the form to the printer. r = PrintClient(Printer.hDC, Printer.CurrentX, Printer.CurrentY, Printer.ScaleWidth, Printer.ScaleHeight, Form1.hWnd) ' Display an error if the return value from PrintWindow is zero. If Not r Then MsgBox "Unable to print the form" Else Printer.EndDoc End If Screen.MousePointer = 0 End Sub 8. Add the following code to the Command2_Click event: Sub Command2_Click () ' The ScaleMode must be set to pixels for the PrintWindow ' routine to print correctly. Printer.ScaleMode = 3 ' Change MousePointer to an hourglass. Screen.MousePointer = 11 ' Initialize the printer. Printer.Print "" ' Establish the location of the printed image. Printer.CurrentX = 100 Printer.CurrentY = 100 ' Copy the image of the form to the printer. r = PrintWindow(Printer.hDC, Printer.CurrentX, Printer.CurrentY, Printer.ScaleWidth, Printer.ScaleHeight, Form1.hWnd) ' Display an error if the return value from PrintWindow is zero. If Not r Then MsgBox "Unable to print the form" Else Printer.EndDoc End If Screen.MousePointer = 0 End Sub Run the program. Choosing Command1 prints just the client area of form1. Choosing Command2 prints the entire area of the form. Note that you can print any of the forms in a project using this method and control the size and placement of the forms by changing the final parameters of the StretchDIBits call. References: Microsoft Windows Programmer's Reference.